<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/audiojs/1.0.0/audio.min.js"></script>
</head>

<body>
    <button id="startBtn" onclick="startPitchDetection()">开始检测</button>
    <div id="pitchDisplay">当前音高: 0 Hz</div>
</body>
<script>
    let aubio;
    let pitchDetector;
    // 全局模式下 aubio 会自动挂载到 window 对象
    async function loadAubio() {
        return new Promise((resolve, reject) => {
            if (window.aubio) {
                resolve(window.aubio);
            } else {
                // 等待库加载完成（例如延迟加载时）
                const checkInterval = setInterval(() => {
                    if (window.aubio) {
                        clearInterval(checkInterval);
                        resolve(window.aubio);
                    }
                }, 100);
            }
        });
    }
    async function initAubio() {
        aubio = await loadAubio(); // 加载 aubio.js
        const sampleRate = 44100; // 需与音频上下文采样率一致
        const bufferSize = 1024;  // 缓冲区大小（需为 2 的幂）

        // 创建 Pitch 检测器（方法可选：'yin' 或 'schmitt'）
        pitchDetector = new aubio.Pitch('yin', bufferSize, bufferSize, sampleRate);
    }

    let audioContext;
    let microphoneStream;

    async function startPitchDetection() {
        // 初始化 aubio
        await initAubio();

        // 获取麦克风权限
        microphoneStream = await navigator.mediaDevices.getUserMedia({ audio: true });

        // 创建音频上下文
        audioContext = new (window.AudioContext || window.webkitAudioContext)({
            sampleRate: 44100, // 与 aubio 的 sampleRate 一致
        });


        // 连接麦克风输入到音频处理节点
        const source = audioContext.createMediaStreamSource(microphoneStream);
        const processor = audioContext.createScriptProcessor(bufferSize, 1, 1);

        source.connect(processor);
        processor.connect(audioContext.destination);



        // 实时处理音频数据
        processor.onaudioprocess = (event) => {
            const inputBuffer = event.inputBuffer.getChannelData(0); // 获取输入音频数据
            const pitch = pitchDetector.do(inputBuffer); // 检测音高
            if (pitch > 0) {
                console.log(`Pitch: ${pitch.toFixed(2)} Hz`);
            }
        };

        // 修改 onaudioprocess 回调以更新界面
        processor.onaudioprocess = (event) => {
            const inputBuffer = event.inputBuffer.getChannelData(0);
            const pitch = pitchDetector.do(inputBuffer);
            if (pitch > 0) {
                document.getElementById("pitchDisplay").textContent =
                    `当前音高: ${pitch.toFixed(2)} Hz (${hzToNote(pitch)})`;
            }
        };

    }

    // 将频率转换为音符名称（例如 440 Hz → A4）
    function hzToNote(freq) {
        const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
        const A4 = 440; // 基准频率 A4 = 440 Hz
        const halfSteps = Math.round(12 * Math.log2(freq / A4));
        const octave = Math.floor(halfSteps / 12) + 4;
        const noteIndex = (halfSteps % 12 + 12) % 12;
        return notes[noteIndex] + octave;
    }

    function stopPitchDetection() {
        if (microphoneStream) {
            microphoneStream.getTracks().forEach(track => track.stop());
        }
        if (audioContext) {
            audioContext.close();
        }
    }

    // document.querySelector("#stopBtn").addEventListener("click", stopPitchDetection);
    // // 启动检测
    // document.querySelector("#startBtn").addEventListener("click", startPitchDetection);
</script>

</html>